From 0bbc8c5421bea9062a7c8db102b635d24984a612 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:52:26 +0000 Subject: [PATCH] [HVM] qemu mouse: Adds support for USB mouse/tablet status check and restricts Universal Host Controller interrupt generating when received NAK in interrupt transfer. According to usb spec, USB mouse/tablet device returns NAK to host controller if its status does not alter in interrupt transfer. And UHC should leave a TD active when receiving NAK and execute this incompleted TD in a subseqent frame. UHC only generates an interrupt on complete after the TD with ICO bit is completed. This patch make UHC & USB mouse/tablet behave consistently with spec. Signed-off-by: Xinmei Huang --- tools/ioemu/hw/usb-hid.c | 19 +++++++++++++++---- tools/ioemu/hw/usb-uhci.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/tools/ioemu/hw/usb-hid.c b/tools/ioemu/hw/usb-hid.c index 8fc0b744b7..60849a4ed0 100644 --- a/tools/ioemu/hw/usb-hid.c +++ b/tools/ioemu/hw/usb-hid.c @@ -39,6 +39,7 @@ typedef struct USBMouseState { int x, y; int kind; int mouse_grabbed; + int status_changed; } USBMouseState; /* mostly the same values as the Bochs USB Mouse device */ @@ -231,6 +232,7 @@ static void usb_mouse_event(void *opaque, s->dy += dy1; s->dz += dz1; s->buttons_state = buttons_state; + s->status_changed = 1; } static void usb_tablet_event(void *opaque, @@ -242,6 +244,7 @@ static void usb_tablet_event(void *opaque, s->y = y; s->dz += dz; s->buttons_state = buttons_state; + s->status_changed = 1; } static inline int int_clamp(int val, int vmin, int vmax) @@ -483,10 +486,16 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, switch(pid) { case USB_TOKEN_IN: if (devep == 1) { - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, data, len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, data, len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + + if (!s->status_changed) + ret = USB_RET_NAK; + else + s->status_changed = 0; + } else { goto fail; } @@ -523,6 +532,7 @@ USBDevice *usb_tablet_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_TABLET; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); @@ -544,6 +554,7 @@ USBDevice *usb_mouse_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_MOUSE; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); diff --git a/tools/ioemu/hw/usb-uhci.c b/tools/ioemu/hw/usb-uhci.c index 4b8db28717..9503c51c3e 100644 --- a/tools/ioemu/hw/usb-uhci.c +++ b/tools/ioemu/hw/usb-uhci.c @@ -424,12 +424,10 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) uint8_t buf[2048]; int len, max_len, err, ret; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; + if (!(td->ctrl & TD_CTRL_ACTIVE)){ + ret = 1; + goto out; } - - if (!(td->ctrl & TD_CTRL_ACTIVE)) - return 1; /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -467,7 +465,8 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); - return -1; + ret = -1; + goto out; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -479,10 +478,12 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - return 1; + ret = 1; + goto out; } else { /* success */ - return 0; + ret = 0; + goto out; } } else { switch(ret) { @@ -501,23 +502,34 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); - return 1; + ret = 1; + goto out; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) goto do_timeout; - return 1; + ret = 1; + goto out; case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; - return 1; + ret = 1; + goto out; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; /* frame interrupted */ - return -1; + ret = -1; + goto out; } } + +out: + /* If TD is inactive and IOC bit set to 1 then update int_mask */ + if ((td->ctrl & TD_CTRL_IOC) && (!(td->ctrl & TD_CTRL_ACTIVE))) { + *int_mask |= 0x01; + } + return ret; } static void uhci_frame_timer(void *opaque) -- 2.30.2